<template>
  <el-form :inline="!!inline" class="forms" :label-width="label_width">
    <template v-for="row in config" >
      <input v-if="row.type=='hidden'&&!row.remove" type="hidden" :name="row.name" :value="row.value" />
      <div v-else-if="row.type=='br'" style="height: 0;width: 100%;"></div>
      <el-form-item v-else-if="!row.remove" :label="row.label" :label-width="row.label_width" :style="form_item_style(row)">
        <div style="width: 100%;">
          <template v-if="$slots[row.type]">
            <slot :name="row.type" :row="row" :value="row.value"></slot>
          </template>
          <unite-param-tabs v-else-if="Array.isArray(row.type)" v-bind="bind_row(row)" :_type_="row.type" :name="remove_name?'':row.name" v-model="row.value" @change="onchange(row, $event)"></unite-param-tabs>
          <unite-param v-else v-bind="bind_row(row)" :_type_="row.type" :name="remove_name?'':row.name" v-model="row.value" @change="onchange(row, $event)"></unite-param>

          <p v-if="row.tips" class="tips" v-html="br(row.tips)"></p>
          <p v-if="row.name in errors" class="error" v-html="errors[row.name]"></p>
        </div>
      </el-form-item>
      <div v-if="row.br" style="height: 0;width: 100%;"></div>
    </template>
    <el-form-item class="btns">
      <div class="flex" style="width: 100%;justify-content: flex-end;">
        <el-button v-for="row in this_btns" :type="row.type" :size="row.size" @click="row.onclick">{{ row.title }}</el-button>
      </div>
    </el-form-item>
  </el-form>
</template>

<script>
import {string_width} from "@/js/dom_tool.js";
import s94Mjs from "s94-js";
import {field_type} from "@/js/tool.js";
import {decode_btns, laravel_validate} from "@/js/s94_tool.js";
import UnifyParam from "@/components/auto_import/UnifyParam.vue";
import UnifyParamTabs from "@/components/auto_import/UnifyParamTabs.vue";

export default {
  name: "forms",
  components: {UniteParamTabs: UnifyParamTabs, UniteParam: UnifyParam},
  props: ['config','btns','remove_name','inline'],
  emits: ['submit:forms','change'],
  data(){
    return {
      init_value_json: '[]',
      errors: {},
    }
  },
  methods: {
    form_item_style(row){
      let res = (row.style && typeof row.style === 'object') ? row.style : {};
      if (row.block) res['display'] = 'flex';
      if (row.inline) res['display'] = 'inline-flex';
      return res;
    },
    bind_row(row){
      let res = Object.assign({}, row);
      delete res.type;
      delete res.name;
      delete res.value;
      delete res.label;
      delete res.remove;
      delete res.tips;
      return res;
    },
    br(str){
      return str.replace(/\n/g,'</br>')
    },
    is_component(type){
      let map = field_type();
      return type in map;
    },
    onchange(row, v) {
      this.$emit('change', this.config);
      if (typeof(row.onchange) == 'function') row.onchange(row, v);
    },
    /**表达验证，验证规则为每个字段的 validate 参数(array|json)
     * @param {array} [name_arr] 需要验证的字段名(name)集，不传表示所有都验证
     * @returns {boolean|Object} 如果验证通过，返回 true。不通过，返回错误信息集(array)
     */
    validate(name_arr){
      let app = this, input = {}, attr = {}, rule = {};
      let config = !this.config || typeof(this.config)!='object' ? {} : this.config;
      s94Mjs.each(config, function (row){
        let need_validate = (typeof(name_arr)=='undefined' || ~name_arr.indexOf(row.name)) && !row.remove;
        if (!need_validate) return;
        app.errors[row.name] = '';
        input[row.name] = row.value;
        attr[row.name] = row.label;
        rule[row.name] = typeof(row.validate) == 'string' ? JSON.parse(row.validate) : row.validate;
      })
      try {
        laravel_validate(input, rule, {}, attr);
        return true;
      } catch (e) {
        if (!e.errors) throw e;
        s94Mjs.each(e.errors, function (v,k){
          app.errors[k] = v.join(' | ');
        })
        return e.errors;
      }
    },
    form_data(){
      return Object.values(this.config).reduce((res,row)=>{
        if (row.name) res[row.name] = row.value;
        return res;
      },{});
    },
    submit(){
      this.errors = {};
      if (this.validate() !== true) return console.error('表单验证不通过');
      let data = new FormData(this.$el);
      this.$emit('submit:forms',data);
    },
    init(init_value){
      this.errors = {};
      let value_arr = JSON.parse(this.init_value_json);
      if (typeof(init_value)=='object') {
        Object.assign(value_arr, init_value);
        this.init_value_json = JSON.stringify(value_arr);
      }
      for (const k in this.config) {
        let name = this.config[k].name;
        if (!name) continue;
        if (typeof(value_arr[name])!='undefined') this.config[k].value = value_arr[name];
      }
    },
    set_init_value(){
      let init_value = {};
      for (const k in this.config) {
        let name = this.config[k].name;
        if (!name) continue;
        init_value[name] = typeof(this.config[k]['value'])=='undefined' ? '' : this.config[k]['value'];
      }
      this.init_value_json = JSON.stringify(init_value);
    },
  },
  computed: {
    this_btns(){
      let app = this;
      if (!this.btns) {
        return [
          {title:'重置', type:'', onclick(){ app.init(); }},
          {title:'提交', type:'primary', onclick(){ app.submit(); }},
        ]
      }else {
        return decode_btns(this.btns);
      }
    },
    label_width(){
      let max = 0;
      Object.keys(this.config).forEach((k)=>{
        if (~["hidden","br"].indexOf(this.config[k].type)) return;
        if (this.config[k].remove) return;
        let label = s94Mjs.to_string(this.config[k].label);
        max = Math.max(string_width(label, '14px'), max);
      })
      return (max+20)+'px';
    },
  },
  mounted() {
    this.set_init_value();
  }
}
</script>

<style scoped>
.el-form{position: relative;overflow: hidden auto;width: 100%;max-height: 100%;background: var(--color-bg);border-radius: 8px;}
.el-form-item{margin: 0 9px;padding: 10px 0;}
.el-form-item .tips{font-size: 0.8em;line-height: 1.5;color: var(--sm_bg);}
.el-form-item .error{font-size: 0.8em;line-height: 1.5;color: var(--fail);}
.el-form-item:hover>.tips{color: var(--el-color-primary-light-3);}
.el-form-item.btns{position: sticky;bottom: 0;z-index: 9;background: var(--color-bg);}
</style>